/*  

Cafechess Library: Cover basic chess programming needs, like GUI, Engine integration, 
FEN and PGN parsing.

                 \\|//
                 (o o)
--------------ooO-(_)-Ooo----------------------------------
Copyright (C) 2009  By Gregory A. Prentice Snoqualmie, WA
                    www.cafechess.org
                    cafechess@comcast.net
                      Oooo
--------------oooO----(  )---------------------------------
              (  )    (_/
               \_)

Microsoft Public License (Ms-PL)
               
Revisions:

Author        Date    Description:

*/
using System;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Drawing.Text;
using System.Windows.Forms;

namespace Cafechess.Imaging.Draw
{
  #region GraphicSettings
  public struct GraphicSettings
  {
    public Color BackColor;
    public Color ForeColor;
    public Color TextColor;
    public Color BorderColor;
    public int X;
    public int Y;
    public int Width;
    public int Height;
    public int Orientation;
    public int PenWidth;
    public float Radius;
    public int AlphaBlending;
    public bool ApplyBorder;
  }
  #endregion

	/// <summary>
	/// Summary description for DrawBallon.
	/// </summary>
	public class DrawBalloon : DrawOnMouseEvents
	{
    GraphicSettings coSettings;
    private Bitmap coDrawing;

    /// <summary>
    /// The default values used for drawing the balloon
    /// </summary>
    public GraphicSettings Settings
    {
      get{ return coSettings; }
      set{ coSettings = value; }
    }
    public string Text
    {
      get{ return coText;}
      set{ coText = value;}
    }
    public int Orientation
    {
      get{ return coSettings.Orientation; }
      set{ coSettings.Orientation = value; }
    }
    public int AlphaBlending
    {
      set{coSettings.AlphaBlending = value; }
    }
    public Font TextFont
    {
      get{ return coTextFont; }
      set{ coTextFont = value; }

    }

    override public Rectangle Square
    {
      get
      {        
        return base.Square;
      }
      set
      {
        IsRedraw = true;
        base.Square = value;
        coSettings.X = value.X;
        coSettings.Y = value.Y;
        coSettings.Height = value.Height;
        coSettings.Width = value.Width;
      }
    }


    Font  coTextFont;
    string coText;


		public DrawBalloon(System.Windows.Forms.Control aControl) : base(aControl)
		{
      coTextFont = new Font("Arial",10);
      coText = "default";
		}
    public void DrawRoundRect(Graphics g, GraphicSettings settings )

    {
      Brush br= null;
      if(settings.AlphaBlending <= 0 )
        settings.AlphaBlending = 255;             
      if( Pen == null )
      {
        if( settings.ApplyBorder )
        {
          br = new SolidBrush(Color.FromArgb(settings.AlphaBlending,settings.BorderColor));
        }
        else
        {
          br = new SolidBrush(Color.FromArgb(settings.AlphaBlending,settings.BackColor));
        }
        Pen = new Pen(br,settings.PenWidth);
        br.Dispose();
      }
      GraphicsPath gp=new GraphicsPath();

      Rectangle innerRect = new Rectangle(settings.X,settings.Y,settings.Width,settings.Height);

      float YplusRadius = settings.Y + settings.Radius;
      float XplusRadius = settings.X + settings.Radius;
      float RminusRadius = innerRect.Right - settings.Radius;
      float RadiusX2 = settings.Radius * 2;
      float HalfWidth = (innerRect.Width - RadiusX2) / 2;
      float HalfHeight = (innerRect.Height - RadiusX2) / 2;
     
      int wd = 6;
      int hg = 8;
      switch(settings.Orientation)
      {
        case 1:
          gp.AddLine(XplusRadius, settings.Y, XplusRadius - hg , settings.Y - hg);      
          gp.AddLine(XplusRadius - hg , settings.Y - hg, XplusRadius + wd , settings.Y);
          gp.AddLine(XplusRadius + wd, settings.Y, RminusRadius, settings.Y);
          break;
        case 2:
          gp.AddLine(XplusRadius, settings.Y, XplusRadius + HalfWidth - (wd/2), settings.Y);
          gp.AddLine(XplusRadius + HalfWidth - (wd/2), settings.Y, XplusRadius + HalfWidth, settings.Y - hg);      
          gp.AddLine(XplusRadius + HalfWidth, settings.Y-hg, XplusRadius + HalfWidth + (wd/2), settings.Y);      
          gp.AddLine(XplusRadius + HalfWidth + (wd/2), settings.Y, RminusRadius, settings.Y);
          break;
        case 3:
          gp.AddLine(XplusRadius, settings.Y, RminusRadius - wd , settings.Y);
          gp.AddLine(RminusRadius - wd , settings.Y, RminusRadius + hg , settings.Y-hg);
          gp.AddLine(RminusRadius + hg , settings.Y-hg, RminusRadius, settings.Y);
          break;
        default:
          gp.AddLine(XplusRadius, innerRect.Y, innerRect.Right - RadiusX2, innerRect.Y);
          break;
      }
      gp.AddArc(innerRect.Right - RadiusX2, innerRect.Y, RadiusX2, RadiusX2, 270, 90);

      switch(settings.Orientation)
      {
        case 4:
          gp.AddLine(innerRect.Right, innerRect.Top + settings.Radius, innerRect.Right + hg, innerRect.Top + settings.Radius - hg);
          gp.AddLine(innerRect.Right + hg, innerRect.Top + settings.Radius - hg, innerRect.Right, innerRect.Top + settings.Radius + wd);
          gp.AddLine(innerRect.Right, innerRect.Top + settings.Radius + wd, innerRect.Right, innerRect.Bottom - settings.Radius);
          break;
        case 5:
          gp.AddLine(innerRect.Right, innerRect.Top + settings.Radius, innerRect.Right, innerRect.Top+ settings.Radius + HalfHeight - (wd/2));
          gp.AddLine(innerRect.Right, innerRect.Top+ settings.Radius + HalfHeight - (wd/2), innerRect.Right+hg, innerRect.Top+ settings.Radius + HalfHeight);
          gp.AddLine(innerRect.Right+hg, innerRect.Top+ settings.Radius + HalfHeight, innerRect.Right, innerRect.Top+ settings.Radius + HalfHeight + (wd/2)); 
          gp.AddLine(innerRect.Right, innerRect.Top+ settings.Radius + HalfHeight + (wd/2), innerRect.Right,innerRect.Bottom - settings.Radius);
          break;
        case 6:
          gp.AddLine(innerRect.Right, innerRect.Top + settings.Radius, innerRect.Right, innerRect.Bottom - settings.Radius - wd);
          gp.AddLine(innerRect.Right, innerRect.Bottom - settings.Radius - wd, innerRect.Right + hg, innerRect.Bottom - settings.Radius + hg);
          gp.AddLine(innerRect.Right + hg, innerRect.Bottom - settings.Radius + hg, innerRect.Right, innerRect.Bottom - settings.Radius);
          break;
        default:
          // Right side
          gp.AddLine(innerRect.Right, YplusRadius, innerRect.Right, innerRect.Bottom - settings.Radius);
          break;
      }
      gp.AddArc(innerRect.Right - RadiusX2, innerRect.Bottom - RadiusX2, RadiusX2, RadiusX2,0,90);
      
      switch(settings.Orientation)
      {
        case 7:
          gp.AddLine(RminusRadius, innerRect.Bottom, RminusRadius + hg , innerRect.Bottom + hg);      
          gp.AddLine(RminusRadius + hg , innerRect.Bottom + hg, RminusRadius - wd, innerRect.Bottom);
          gp.AddLine(RminusRadius - wd, innerRect.Bottom, XplusRadius, innerRect.Bottom);
          break;
        case 8:
          gp.AddLine(RminusRadius, innerRect.Bottom, RminusRadius - HalfWidth + (wd/2),innerRect.Bottom);
          gp.AddLine(RminusRadius - HalfWidth + (wd/2),innerRect.Bottom, RminusRadius - HalfWidth, innerRect.Bottom + hg);
          gp.AddLine(RminusRadius - HalfWidth, innerRect.Bottom + hg, XplusRadius + HalfWidth - (wd/2),innerRect.Bottom);
          gp.AddLine(XplusRadius + HalfWidth - (wd/2),innerRect.Bottom, XplusRadius, innerRect.Bottom);      
          break;
        case 9:
          gp.AddLine(RminusRadius, innerRect.Bottom, XplusRadius + wd , innerRect.Bottom);      
          gp.AddLine(XplusRadius + wd , innerRect.Bottom, XplusRadius - wd, innerRect.Bottom + hg);
          gp.AddLine(XplusRadius - wd, innerRect.Bottom + hg, XplusRadius, innerRect.Bottom);
          break;
        default:
          // Bottom
          gp.AddLine(innerRect.Right - settings.Radius, innerRect.Bottom, XplusRadius, innerRect.Bottom);
          break;
      }
      gp.AddArc(settings.X, innerRect.Bottom - RadiusX2, RadiusX2, RadiusX2, 90, 90);
      switch(settings.Orientation)
      {
        case 10:
          gp.AddLine(innerRect.Left, innerRect.Bottom - settings.Radius, innerRect.Left - hg, innerRect.Bottom - settings.Radius + hg);
          gp.AddLine(innerRect.Left - hg, innerRect.Bottom - settings.Radius + hg, innerRect.Left, innerRect.Bottom - settings.Radius - wd);
          gp.AddLine(innerRect.Left, innerRect.Bottom - settings.Radius - wd, innerRect.Left, innerRect.Top + settings.Radius);
          break;
        case 11:
          gp.AddLine(innerRect.Left, innerRect.Bottom - settings.Radius, innerRect.Left, innerRect.Bottom - settings.Radius - HalfHeight + (wd/2));
          gp.AddLine(innerRect.Left, innerRect.Bottom - settings.Radius - HalfHeight + (wd/2), innerRect.Left - hg, innerRect.Bottom - settings.Radius - HalfHeight );
          gp.AddLine(innerRect.Left - hg, innerRect.Bottom - settings.Radius - HalfHeight , innerRect.Left, innerRect.Top + settings.Radius + HalfHeight - (wd/2)); 
          gp.AddLine(innerRect.Left, innerRect.Top + settings.Radius + HalfHeight - (wd/2), innerRect.Left ,innerRect.Top + settings.Radius);
          break;
        case 12:
          gp.AddLine(innerRect.Left, innerRect.Bottom - settings.Radius, innerRect.Left, innerRect.Top + settings.Radius+wd);
          gp.AddLine(innerRect.Left, innerRect.Top + settings.Radius+wd, innerRect.Left-hg, innerRect.Top + settings.Radius - hg);
          gp.AddLine(innerRect.Left-hg, innerRect.Top + settings.Radius - hg, innerRect.Left, innerRect.Top + settings.Radius);          
          
          break;
        default:
          // Left Side
          gp.AddLine(innerRect.Left, innerRect.Bottom - settings.Radius, innerRect.Left, YplusRadius);
          break;
      }
      gp.AddArc(innerRect.X, innerRect.Y, RadiusX2, RadiusX2, 180, 90);

      gp.CloseFigure();
 
      br = new SolidBrush(Color.FromArgb(settings.AlphaBlending,settings.BackColor));
      g.CompositingQuality = CompositingQuality.GammaCorrected;
      g.FillPath(br,gp);
      br.Dispose();
      g.DrawPath(Pen, gp);

      if(coText != null && coText.Length > 0)
      {
        // If our image has text, draw it using the Font passed in and center the
        // text vertically and horizontally in our ballon.
        SolidBrush TextBrush = new SolidBrush(Color.FromArgb(255,settings.TextColor));
        StringFormat sf = new StringFormat();
        sf.Alignment = StringAlignment.Center;
        sf.Trimming = StringTrimming.EllipsisCharacter; 
        sf.LineAlignment = StringAlignment.Center;
        g.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; 
        g.DrawString(coText,coTextFont,TextBrush,innerRect,sf);
        TextBrush.Dispose();
        sf.Dispose();
      }
      gp.Dispose();
    }

    override public void DrawFinished(Graphics g)
    {
      if(IsRedraw)
      {
        if( coDrawing != null )
          coDrawing.Dispose();
        coDrawing = new Bitmap(coSettings.Width + 16,coSettings.Height + 16);
        Graphics ig = Graphics.FromImage(coDrawing);
        int x = coSettings.X;
        int y = coSettings.Y;
        coSettings.X = 8;
        coSettings.Y = 8;        
        DrawRoundRect(ig,coSettings);
        coSettings.X = x;
        coSettings.Y = y;
        ig.Dispose();
        IsRedraw = false;
      }
      if(coDrawing != null )
        g.DrawImage(coDrawing,coSettings.X,coSettings.Y);
    }
    public override bool HitTest(Point point)
    {
      return false;
    }
    override protected void DisposeUnmanaged()
    {
      if( ! IsDisposed )
      {
        base.DisposeUnmanaged();
        if(coDrawing != null)
        {
          coDrawing.Dispose();
          coDrawing = null;
        }
      }
    }
	}
}
